home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 6 code / TCP / NewsWatcher / NW Source / Source / search.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-26  |  8.7 KB  |  353 lines  |  [TEXT/MMCC]

  1. /*----------------------------------------------------------------------------
  2.  
  3.     search.c
  4.  
  5.     This module handles the "Search" command.
  6.     
  7.     Copyright © 1994-1995, Northwestern University.
  8.  
  9. ----------------------------------------------------------------------------*/
  10.  
  11. #include <string.h>
  12. #include <ctype.h>
  13. #include <stdio.h>
  14.  
  15. #include "glob.h"
  16. #include "search.h"
  17. #include "dialog.h"
  18. #include "newswatcher.h"
  19. #include "mark.h"
  20. #include "menus.h"
  21. #include "news.h"
  22. #include "group.h"
  23. #include "popuputil.h"
  24. #include "subscribe.h"
  25. #include "status.h"
  26. #include "memutil.h"
  27. #include "strutil.h"
  28. #include "header.h"
  29. #include "full.h"
  30.  
  31.  
  32.  
  33. #define kSearchDlg            140            /* Search dialog */
  34. #define kSearchHeader        3
  35. #define    kSearchPattern        4
  36. #define kSearchPopup        5
  37.  
  38.  
  39.  
  40. /*----------------------------------------------------------------------------
  41.     DoSearchDialog
  42.     
  43.     Present the search dialog.
  44.             
  45.     Exit:    function result = error code.
  46.             header = header name.
  47.             pattern = search string.
  48. ----------------------------------------------------------------------------*/
  49.  
  50. static OSErr DoSearchDialog (CStr255 header, CStr255 pattern)
  51. {
  52.     static CStr255 headerSave = "";
  53.     static CStr255 patternSave = "";
  54.     DialogPtr dlg = nil;
  55.     short item;
  56.     OSErr err = noErr;
  57.     
  58.     if (*headerSave == 0) GetCString(kStrDefaultSearchHdr, headerSave);
  59.     
  60.     err = MyGetNewDialog(kSearchDlg, ok, cancel, &dlg);
  61.     if (err != noErr) return err;
  62.     RestoreMovableModalDialogPosition(dlg, gPrefs.searchLoc);
  63.     
  64.     strcpy(header, headerSave);
  65.     strcpy(pattern, patternSave);
  66.  
  67.     DlgSetCString(dlg, kSearchHeader, header);
  68.     SetItemKeyword(dlg, kSearchHeader);
  69.     SetItemMaxLength(dlg, kSearchHeader, 255);
  70.     DlgSetCString(dlg, kSearchPattern, pattern);
  71.     SetItemMaxLength(dlg, kSearchPattern, 255);
  72.     SelectDialogItemText(dlg, kSearchPattern, 0, 255);
  73.     SetItemPopupTypeinItem(dlg, kSearchPopup, kSearchHeader);
  74.     
  75.     do {
  76.         DlgEnableItem(dlg, ok, *header != 0 && *pattern != 0);
  77.         MyMovableModalDialog(dlg, DialogFilter, &item);
  78.         switch (item) {
  79.             case kSearchPopup:
  80.             case kSearchHeader:
  81.                 DlgGetCString(dlg, kSearchHeader, header);
  82.                 break;
  83.             case kSearchPattern:
  84.                 DlgGetCString(dlg, kSearchPattern, pattern);
  85.                 break;
  86.         }
  87.     } while (item != ok && item != cancel);
  88.     
  89.     SaveMovableModalDialogPosition(dlg, &gPrefs.searchLoc);
  90.     err = DoClose(dlg);
  91.     if (err != noErr) return err;
  92.     
  93.     if (item == cancel) return userCanceledErr;
  94.     
  95.     strcpy(headerSave, header);
  96.     strcpy(patternSave, pattern);
  97.     return noErr;
  98. }
  99.  
  100.  
  101.  
  102. /*----------------------------------------------------------------------------
  103.     SearchOneGroup 
  104.     
  105.     Search a single group.
  106.     
  107.     Entry:    header = name of the header to be searched.
  108.             pattern = search string.
  109.             *theGroup = group record.
  110.             
  111.     Exit:    function result = error code.
  112.             theGroup->numUnread = number of matched articles.
  113.             theGroup->unread = handle to unread list.
  114. ----------------------------------------------------------------------------*/
  115.  
  116. static OSErr SearchOneGroup (char *header, char *pattern, TGroup *theGroup)
  117. {
  118.     CStr255    groupName, statusStr;
  119.     THeader **headers = nil;
  120.     short numHeaders;
  121.     THeader *pHeader, *pHeaderEnd;
  122.     long firstUnread, lastUnread;
  123.     long number;
  124.     OSErr err = noErr;
  125.     Boolean groupExists;
  126.  
  127.     strcpy(groupName, *gGroupNames + theGroup->nameOffset);
  128.  
  129.     GetCString(kStrSearchingStatusMsg, statusStr);
  130.     strcat(statusStr, groupName);
  131.     err = DisplayStatusMessage(statusStr);
  132.     if (err != noErr) return err;
  133.     
  134.     err = GetGroupArticleRange(theGroup, &groupExists);
  135.     if (err != noErr) return err;
  136.     if (!groupExists) return noErr;
  137.     
  138.     theGroup->numUnread = 0;
  139.     err = SearchHeaders(groupName, header, theGroup->firstMess,
  140.         theGroup->lastMess, pattern, &headers, &numHeaders);
  141.     if (err != noErr) return err;
  142.     if (headers == nil) return noErr;
  143.  
  144.     MyHLock(headers);
  145.     pHeaderEnd = *headers + numHeaders;
  146.     firstUnread = 0;
  147.     for (pHeader = *headers; pHeader < pHeaderEnd; pHeader++) {
  148.         number = pHeader->number;
  149.         if (firstUnread == 0) {
  150.             firstUnread = lastUnread = number;
  151.         } else if (pHeader->number == lastUnread+1) {
  152.             lastUnread = number;
  153.         } else {
  154.             err = AppendUnreadRange(firstUnread, lastUnread, theGroup);
  155.             if (err != noErr) goto exit;
  156.             firstUnread = lastUnread = number;
  157.         }
  158.     }
  159.     if (firstUnread != 0) {
  160.         err = AppendUnreadRange(firstUnread, lastUnread, theGroup);
  161.         if (err != noErr) goto exit;
  162.     }
  163.     MyDisposeHandle(headers);
  164.     return noErr;
  165.     
  166. exit:
  167.  
  168.     MyDisposeHandle(headers);
  169.     return err;
  170. }
  171.  
  172.  
  173.  
  174. /*----------------------------------------------------------------------------
  175.     SearchGroups
  176.     
  177.     Search groups on the server and build a new group array containing
  178.     the matching groups and article lists.
  179.     
  180.     Entry:    wind = pointer to group, subject, or article window.
  181.             header = header name.
  182.             pattern = search string.
  183.     
  184.     Exit:    function result = error code.
  185.             *newGroupArray = array of matching group records.
  186.             *newNumGroups = number of matching group records.
  187. ----------------------------------------------------------------------------*/
  188.  
  189. static OSErr SearchGroups (WindowPtr wind, char *header, char *pattern,
  190.     TGroup ***newGroupArray, short *newNumGroups)
  191. {
  192.     TWindow **info;
  193.     TWindowKind kind;
  194.     ListHandle theList;
  195.     Cell theCell;
  196.     short index, cellDataLen;
  197.     TGroup **groupArray;
  198.     TGroup theGroup; 
  199.     TGroup **theNewGroupArray = nil;
  200.     short theNewNumGroups = 0;
  201.     short numAllocated;
  202.     OSErr err = noErr;
  203.     Boolean done = false;
  204.     Boolean firstTime = true;
  205.     Handle newsgroups = nil;
  206.     long i, j, k, len;
  207.     CStr255 groupName;
  208.     char c;
  209.  
  210.     info = (TWindow**)GetWRefCon(wind);
  211.     kind = (**info).kind;
  212.     
  213.     err = MyNewHandle(100*sizeof(TGroup), &theNewGroupArray);
  214.     if (err != noErr) goto exit;
  215.     numAllocated = 100;
  216.     
  217.     switch (kind) {
  218.         case kGroup:
  219.             theList = (**info).theList;
  220.             groupArray = (**info).groupArray;
  221.             SetPt(&theCell, 0, 0);
  222.             break;
  223.         case kSubject:
  224.             break;
  225.         case kArticle:
  226.             err = FindHeaderHandle((**info).fullText, "Newsgroups", &newsgroups);
  227.             if (err != noErr) goto exit;
  228.             len = MyGetHandleSize(newsgroups);
  229.             i = 0;
  230.             break;
  231.     }
  232.     
  233.     while (!done) {
  234.         switch (kind) {
  235.             case kGroup:
  236.                 done = !LGetSelect(true, &theCell, theList);
  237.                 if (done) break;
  238.                 cellDataLen = 2;
  239.                 LGetCell(&index, &cellDataLen, theCell, theList);
  240.                 theGroup.nameOffset = (*groupArray)[index].nameOffset;
  241.                 theCell.v++;
  242.                 break;
  243.             case kSubject:
  244.                 done = !firstTime;
  245.                 if (done) break;
  246.                 firstTime = false;
  247.                 theGroup.nameOffset = (**info).groupNameOffset;
  248.                 break;
  249.             case kArticle:
  250.                 while (true) {
  251.                     while (i < len && !isalnum((*newsgroups)[i])) i++;
  252.                     done = i >= len;
  253.                     if (done) break;
  254.                     j = 0;
  255.                     while (i < len && j < 256) {
  256.                         c = (*newsgroups)[i];
  257.                         if (isalnum(c) || c == '.') {
  258.                             groupName[j++] = c;
  259.                             i++;
  260.                         } else {
  261.                             break;
  262.                         }
  263.                     }
  264.                     done = j >= 256;
  265.                     if (done) break;
  266.                     groupName[j] = 0;
  267.                     k = FindGroupIndex(groupName);
  268.                     if (k != -1) {
  269.                         theGroup.nameOffset = (*gFullGroupArray)[k].nameOffset;
  270.                         break;
  271.                     }
  272.                 }
  273.                 break;
  274.         }
  275.         if (done) break;
  276.         theGroup.firstMess = theGroup.lastMess = theGroup.numUnread = 0;
  277.         theGroup.unread = nil;
  278.         theGroup.onlyRedrawCount = false;
  279.         err = SearchOneGroup(header, pattern, &theGroup);
  280.         if (err != noErr) goto exit;
  281.         if (theGroup.numUnread != 0) {
  282.             if (theNewNumGroups >= numAllocated) {
  283.                 numAllocated += 100;
  284.                 err = MySetHandleSize(theNewGroupArray, numAllocated*sizeof(TGroup));
  285.                 if (err != noErr) goto exit;
  286.             }
  287.             (*theNewGroupArray)[theNewNumGroups] = theGroup;
  288.             theNewNumGroups++;
  289.         }
  290.     }
  291.     
  292.     MySetHandleSize(theNewGroupArray, theNewNumGroups*sizeof(TGroup));
  293.     *newGroupArray = theNewGroupArray;
  294.     *newNumGroups = theNewNumGroups;
  295.     MyDisposeHandle(newsgroups);
  296.     return noErr;
  297.     
  298. exit:
  299.  
  300.     DisposeGroupArray(theNewGroupArray, theNewNumGroups);
  301.     MyDisposeHandle(newsgroups);
  302.     return err;
  303. }
  304.  
  305.  
  306.  
  307. /*----------------------------------------------------------------------------
  308.     DoSearch
  309.     
  310.     Handle the "Search" command.
  311.     
  312.     Entry:    wind = pointer to group, subject, or article window.
  313.     
  314.     Exit:    function result = error code.
  315. ----------------------------------------------------------------------------*/
  316.  
  317. OSErr DoSearch (WindowPtr wind)
  318. {
  319.     CStr255    header, pattern;
  320.     TGroup **groupArray = nil;
  321.     short numGroups;
  322.     WindowPtr newWind;
  323.     TWindow **info;
  324.     Str255 title;
  325.     OSErr err = noErr;
  326.     TSavedWindPos pos;
  327.     
  328.     /* Present the search dialog. */
  329.     
  330.     err = DoSearchDialog(header, pattern);
  331.     if (err != noErr) return err;
  332.     
  333.     /* Search the groups. */
  334.     
  335.     err = SearchGroups(wind, header, pattern, &groupArray, &numGroups);
  336.     if (err != noErr) return err;
  337.     if (numGroups == 0) {
  338.         NoteMessageNumber(kStrNoMatches);
  339.         MyDisposeHandle(groupArray);
  340.         return noErr;
  341.     }
  342.     
  343.     /* Create the window. */
  344.     
  345.     GetPString(kStrSearchWindowTitle, title);
  346.     pos.valid = false;
  347.     err = MakeUserGroupWindow(title, groupArray, numGroups, &pos, &newWind);
  348.     if (err != noErr) return err;
  349.     info = (TWindow**)GetWRefCon(newWind);
  350.     (**info).okToCloseIfChanged = true;
  351.     return noErr;
  352. }
  353.